import logging

import simpy

'''
It is an extension of simpy.FilterStore to model assembly activities . 
Overrode methods enhances the original Python class with adding parameter of 'component' 
which is exclusively for OSC activity, collecting properties such queue and items of the resource.      
'''


# monitor pre-get and pre-put of FilterStore
class MonitoredFilterStore(simpy.FilterStore):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # time, stock level
        self.data = []
        # for logging with cpations
        self.pre_log_data = []
        self.post_log_data = []
        #
        self.pre_data = []
        self.post_data = []
        self.acc_vol = 0

        # set log level
        logging.basicConfig(level=logging.INFO)

    # occupy an item of filterStore
    # component: the component which occupy the resource item
    # operation: a string describing what is going
    def get(self, component, operation, *args, **kwargs):
        # collect pre data
        if component is not None:
            pre_log_data_entry = (
                round(self._env.now, 2), component.name, 'pre ' + operation, 'Q=' + str(len(self.get_queue)),
                'VAC=' + str(len(self.items)))
            # print(pre_log_data_entry)
            self.pre_log_data.append(pre_log_data_entry)
            # number of occupied resource units
            pre_data_entry = (self._env.now, self.capacity - len(self.items), len(self.get_queue))
            self.data.append(pre_data_entry)

            ret = super().get(*args, **kwargs)

            post_log_data_entry = (
                round(self._env.now, 2), component.name, 'a_post ' + operation, 'Q=' + str(len(self.get_queue)),
                'VAC=' + str(len(self.items)))

            # print(post_log_data_entry)
            logging.debug(f'____data entry for a_post get {post_log_data_entry}')
            self.post_log_data.append(post_log_data_entry)
            # time, amount, occupied, queue
            self.acc_vol = self.acc_vol + component.vol
            post_data_entry = [
                round(self._env.now, 2), len(self.items), self.capacity - len(self.items),
                len(self.get_queue), component.vol, self.acc_vol]
            self.post_data.append(post_data_entry)

            # print("@{now:.2f} {name}  {operation} done, return {ret}".format(now=self._env.now,
            #                                                                  operation=operation,
            #                                                                  name=component.name,
            #                                                                  ret=ret
            #                                                                  ))
            # collect a_post data
            # data_entry = (self._env.now, 'a_post stock in',len(self.get_queue), len(self.items))
            # logging.info(f'____data entry for get {data_entry}')
            # self.get_data.append(data_entry)
        return ret

    # release an item in filterStore
    # component: the component which release the resource item
    # operation: a string describing what is going
    def put(self, component, operation, *args, **kwargs):
        # collect pre stock out data
        # if this method is not called with a component parameter,
        # it would be creating slot, instead of releasing slot
        if component is not None:
            put_data_entry = (
                round(self._env.now, 2), component.name, 'pre ' + operation, 'Q=' + str(len(self.put_queue)),
                'VAC=' + str(len(self.items)))
            # accept = para if para > 0 else 0
            # logging.debug(f'____data entry for put {put_data_entry}')
            self.pre_log_data.append(put_data_entry)
            # number of occupied resource units
            data_entry = (self._env.now, self.capacity - len(self.items), len(self.put_queue))
            self.data.append(data_entry)
            ret = super().put(*args, **kwargs)

            put_data_entry = (
                round(self._env.now, 2), component.name, 'a_post ' + operation, 'Q=' + str(len(self.put_queue)),
                'VAC=' + str(len(self.items)))
            # accept = para if para > 0 else 0
            logging.debug(f'____data entry for a_post put {put_data_entry}')
            self.post_log_data.append(put_data_entry)

            # time, amount, occupied, queue
            self.acc_vol = self.acc_vol - component.vol
            post_data_entry = [
                round(self._env.now, 2), len(self.items), self.capacity - len(self.items),
                len(self.get_queue), -component.vol, self.acc_vol]
            self.post_data.append(post_data_entry)

            # print("@{now:.2f} {name}  {operation} done".format(now=self._env.now,
            #                                                    operation=operation,
            #                                                    name=component.name,
            #                                                    ))

        return ret


if __name__ == '__main__':
    env = simpy.Environment()
    filter_store = MonitoredFilterStore(env, capacity=1)

    env.run()

    logging.info(filter_store.pre_log_data)
